﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.IO;
using System.IO.IsolatedStorage;

using Factonomy.AzureUtility.Core.Storage;

namespace Factonomy.AzureUtility.Core
{
    class Config
    {
        private static Config _instance;
        private XmlDocument _doc = new XmlDocument();
        private string _filename = "Factonomy.AzureStorage.Config.xml";

        public static Config Instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = new Config();
                }
                return _instance;
            }
        }

        public XmlNode DocumentElement
        {
            get { return this._doc.DocumentElement; }
        }

        public string Rootpath
        {
            get
            {
                return this.GetSetting("rootpath");
            }
            set
            {
                this.SaveSetting("rootpath", value);
            }
        }

        private string GetSetting(string name)
        {
            XmlNode node = this._doc.SelectSingleNode("config/" + name);
            if (node == null)
            {
                return string.Empty;
            }
            else
            {
                return node.InnerText;
            }
        }

        private void SaveSetting(string name, string value)
        {
            XmlNode node = this._doc.SelectSingleNode("config/" + name);
            if (node != null)
            {
                node.RemoveChild(node.FirstChild);
                XmlCDataSection data = this._doc.CreateCDataSection(value);
                node.AppendChild(data);
            }
            this.Save();
        }

        public Config()
        {
            using (StreamReader stream = new StreamReader(new IsolatedStorageFileStream(this._filename, FileMode.OpenOrCreate, IsolatedStorageFile.GetUserStoreForDomain())))
            {
                if (stream.BaseStream.Length == 0)
                {
                    this._doc.Load(ResourceManager.DefaultConfig);
                }
                else
                {
                    this._doc.Load(stream);
                }
            }
        }

        public void Save()
        {
            IsolatedStorageFile storage = IsolatedStorageFile.GetUserStoreForDomain();
            IsolatedStorageFileStream stream = new IsolatedStorageFileStream(this._filename, System.IO.FileMode.Create, storage);
            StreamWriter writer = new StreamWriter(stream);
            writer.Write(this._doc.OuterXml);
            writer.Flush();
            writer.Close();
            stream.Close();
            storage.Close();
        }

        public void Delete()
        {
            IsolatedStorageFile.GetUserStoreForDomain().DeleteFile(this._filename);
        }

        public List<ConfigAccount> ListAccounts()
        {
            List<ConfigAccount> accounts = new List<ConfigAccount>();
            XmlNodeList accountNodes = this._doc.SelectNodes("config/accounts/account");
            if (accountNodes != null && accountNodes.Count > 0)
            {
                
                foreach (XmlNode accountNode in accountNodes)
                {
                    accounts.Add(new ConfigAccount(accountNode));
                }
            }
            return accounts;
        }

        public void UpsertAccount(string accountName, string sharedKey, bool local, bool enabled)
        {
            XmlNode account = this._doc.SelectSingleNode("config/accounts/account[@id='" + accountName + "']");
            if (account == null)
            {
                this.AddAccount(accountName, sharedKey, local, enabled);
            }
            else
            {
                this.UpdateAccount(accountName, sharedKey, local, enabled);
            }
            this.Save();
        }

        public void UpdateAccount(string accountName, string sharedKey, bool local, bool enabled)
        {
            XmlNode accountNode = this._doc.SelectSingleNode("config/accounts/account[@id='" + accountName + "']");
            this.UpdateAccountSetting(accountNode, "sharedkey", sharedKey);
            this.UpdateAccountSetting(accountNode, "local", local.ToString());
            this.UpdateAccountSetting(accountNode, "enabled", enabled.ToString());
            this.Save();
        }

        private void UpdateAccountSetting(XmlNode account, string setting, string value)
        {
            if (account[setting] == null)
            {
                XmlNode settingNode = account.OwnerDocument.CreateElement(setting);
                settingNode.AppendChild(account.OwnerDocument.CreateCDataSection(value));
                account.AppendChild(settingNode);
            }
            else
            {
                account[setting].InnerText = string.Empty;
                account[setting].AppendChild(this._doc.CreateCDataSection(value));
            }
        }

        public XmlNode CreateAccountNode(string accountName, string sharedKey, bool local, bool enabled)
        {
            XmlDocument doc = new XmlDocument();
            XmlNode accountNode = doc.CreateElement("account");
            accountNode.Attributes.Append(doc.CreateAttribute("id"));
            accountNode.Attributes["id"].Value = accountName;

            doc.AppendChild(accountNode);

            this.UpdateAccountSetting(accountNode, "sharedkey", sharedKey);
            this.UpdateAccountSetting(accountNode, "local", local.ToString());
            this.UpdateAccountSetting(accountNode, "enabled", enabled.ToString());

            return doc.DocumentElement;
        }

        public void AddAccount(string accountName, string sharedKey, bool local, bool enabled)
        {
            XmlNode accountsNode = this._doc.SelectSingleNode("config/accounts");
            accountsNode.AppendChild(this._doc.ImportNode(CreateAccountNode(accountName, sharedKey, local, enabled), true));
            this.Save();
        }

        public void DeleteAccount(string accountName)
        {
            XmlNode account = this._doc.SelectSingleNode("config/accounts/account[@id='" + accountName + "']");
            if (account != null)
            {
                account.ParentNode.RemoveChild(account);
                this.Save();
            }
        }

        public ConfigAccount GetAccount(string accountName)
        {
            XmlNode account = this._doc.SelectSingleNode("config/accounts/account[@id='" + accountName + "']");
            if (account != null)
            {
                return new ConfigAccount(account);
            }

            return null;
        }
    }

    public enum AccountType
    {
        Live,
        Development
    }
}
